package org.sixtythousand.joker;

import org.sixtythousand.joker.util.AbstractLifeCycle;
import org.sixtythousand.joker.util.AutoLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.channels.ClosedByInterruptException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.Condition;

/**
 * @author zhoulk
 * @date 2023/10/11 10:42
 * @desc
 */
public abstract class AbstractConnector extends AbstractLifeCycle implements Connector {
    protected static final Logger LOG = LoggerFactory.getLogger(AbstractConnector.class);
    protected Integer port;
    protected final Server server;
    protected final Map<String, ConnectionFactory> connectionFactories = new LinkedHashMap<>();
    protected final Executor executor;
    protected final AutoLock lock = new AutoLock();
    protected final Condition setAccepting = lock.newCondition();
    protected boolean accepting = true;


    public AbstractConnector(Server server,
                             Executor executor,
                             ConnectionFactory... factories) {
        this.server = server;
        this.executor = executor;

        for (ConnectionFactory factory : factories) {
            factory.getProtocols().forEach(protocol -> connectionFactories.put(protocol, factory));
        }
    }


    @Override
    protected void doStart() {
        super.doStart();
        getExecutor().execute(new Acceptor(1));
        LOG.info("Started {}", this);
    }

    @Override
    public Server getServer() {
        return null;
    }

    @Override
    public Executor getExecutor() {
        return null;
    }

    @Override
    public void addConnectionFactory(String protocol, AbstractConnectionFactory connectorFactory) {

    }

    @Override
    public ConnectionFactory getConnectionFactory(String protocol) {
        return null;
    }

    @Override
    public Collection<ConnectionFactory> getConnectionFactories() {
        return null;
    }

    @Override
    public List<String> getProtocols() {
        return null;
    }

    @Override
    public void stop() {

    }

    /**
     * 接受请求
     */
    protected abstract void accept(Integer id);

    /**
     * 处理接受请求失败
     */
    protected boolean handleAcceptFailure(Throwable ex) {
        if (isRunning()) {
            if (ex instanceof InterruptedException) {
                if (LOG.isDebugEnabled())
                    LOG.debug("Accept Interrupted", ex);
                return true;
            }

            if (ex instanceof ClosedByInterruptException) {
                if (LOG.isDebugEnabled())
                    LOG.debug("Accept Closed by Interrupt", ex);
                return false;
            }

            LOG.warn("Accept Failure", ex);
            try {
                // Arbitrary sleep to avoid spin looping.
                // Subclasses may decide for a different
                // sleep policy or closing the connector.
                Thread.sleep(1000);
                return true;
            } catch (Throwable x) {
                LOG.trace("IGNORED", x);
            }
            return false;
        } else {
            LOG.trace("IGNORED", ex);
            return false;
        }
    }

    ;

    private class Acceptor implements Runnable {
        private final int _id;
        private String _name;

        private Acceptor(int id) {
            _id = id;
        }

        @Override
        public void run() {
            final Thread thread = Thread.currentThread();
            String name = thread.getName();
            _name = String.format("%s-acceptor-%d@%x-%s", name, _id, hashCode(), AbstractConnector.this.toString());
            thread.setName(_name);

            int priority = thread.getPriority();


            try {
                while (isRunning()) {
                    try (AutoLock l = lock.lock()) {
                        if (!accepting && isRunning()) {
                            setAccepting.await();
                            continue;
                        }
                    } catch (InterruptedException e) {
                        continue;
                    }

                    try {
                        accept(_id);
                    } catch (Throwable x) {
                        if (!handleAcceptFailure(x)) {
                            break;
                        }
                    }
                }
            } finally {
                thread.setName(name);
            }
        }

        @Override
        public String toString() {
            String name = _name;
            if (name == null)
                return String.format("acceptor-%d@%x", _id, hashCode());
            return name;
        }
    }
}
